#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

struct Info{   //存放命令行参数
    int c;
    int n;
    char* addr;
    char* port;
    char* url;
}info;

int c = 0;
int n = 0;
int count_success = 0;
pthread_mutex_t mutex;

void help() {
    printf("---------------------------------------------------------------------------\n");
    printf("帮助手册\n");
    printf("通过TCP协议模拟建立HTTP请求\n");
    printf("你可以尝试输入 ./myab -c 线程数 -n ,请求次数 http://IP地址:端口号/[资源路径]\n");
    printf("请保证IP地址和端口号合法， n必须大于等于c, 资源路径可不填\n");
    printf("---------------------------------------------------------------------------\n");
}

int _getop(int argc, char* argv[], struct Info* pinfo) {
    //xxx.xxx.xxx.xxx\0 : xxxxx\0
    pinfo->addr = (char*)malloc(16);
    pinfo->port = (char*)malloc(6); 
    pinfo->url = (char*)malloc(64);
    memset(pinfo->addr, 0, 16);
    memset(pinfo->port, 0, 6);
    memset(pinfo->url, 0, 64);
    if(argc != 2 && argc != 6) {
        //打印帮助手册
        printf("你可以尝试输入 ./myab --help 查看帮助手册\n");
        return -1;
    } else if(argc == 2){
        //argc == 2
        if(strcmp("--help", argv[1]) == 0) {
            help();
            return -1;
        } else {
            printf("输入错误! 你可以尝试输入 ./myab --help 查看帮助手册\n");
            return -1;
        }
    } else {
        if((strcmp(argv[1], "-c") == 0)
           && (strcmp(argv[3], "-n") == 0)
           && (strncmp(argv[5], "http://", 7) == 0)) {
            //此时输入可能正确，还需判断argv[2] > 0,  argv[4] >= argv[2], IP地址和端口号合法
            int c = atoi(argv[2]);
            int n = atoi(argv[4]);
            //printf("c = %d n = %d\n", c, n);
            if(c > n) {
                help();
                return -1;
            }
            pinfo->c = c;
            pinfo->n = n;

            char* tmp = argv[5] + 7;
            int i = 0; 
            while(*tmp != ':') {
                pinfo->addr[i] = *tmp;
                ++i, ++tmp;
            }
            pinfo->addr[i] = '\0';

            for(i=0, ++tmp; *tmp != '\0' && *tmp != '/'; ++i, ++tmp) {
                pinfo->port[i] = *tmp;
            }
            if(*tmp == '/') {
                pinfo->port[i] = '\0';
                *tmp = 0;
                tmp++;
                for(i = 0; *tmp != '\0'; ++i, ++tmp) {
                    pinfo->url[i] = *tmp;
                }
                pinfo->url[i] = '\0';
            } else {
                pinfo->port[i] = '\0';
            }
        } else {
            help();
            return -1;
        }
    }

    return 0;
}

void getlin(int sock, char* line){
    int c = 0;
    int i = 0;
    int n;

    while(c != '\n' && i<1023) {
        n = recv(sock, &c, 1, 0);
        if( n > 0  ) {
            if( c == '\r'   ){//将所有的\r\n(windows) 和 \r(mac os) 统一转化成\n
                recv(sock, &c, 1, MSG_PEEK); //对下一个字符制作检测，在内核接受缓冲区中，并不清除该字符
                if( c != '\n'   ){
                    c = '\n';
                }else{
                    recv(sock, &c, 1, 0);
                }
            }
            line[i++] = c;
            if(c == '\n') {
                break;
            }
        }else{
            break;
        }                                                                                                                                                 
    }
    line[i] = '\0';
    //printf("line : %s",line);
}

void clean_head(int sock) {
    char line[512];
    do
    {
        getlin(sock, line);
    }while(line[0] != '\n');
}

void* handler(void* arg) {
    //printf("创建线程成功\n");
    pthread_detach(pthread_self());
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = *(struct sockaddr_in*)arg;
    if(c > info.c) {
        pthread_mutex_lock(&mutex);  
        c--;//加锁 
        pthread_mutex_unlock(&mutex);  
        return NULL;
    }
    if(n > info.n) {
        pthread_mutex_lock(&mutex);  
        c--;//加锁 
        pthread_mutex_unlock(&mutex);  
        return NULL;
    }

    //得到服务器的文件描述符
    int ret = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0) {
        //printf("connect 失败\n");
        pthread_mutex_lock(&mutex);  
        c--;//加锁 
        pthread_mutex_unlock(&mutex);  
        return NULL;
    } 
    //printf("connect 成功\n");

    //发送http请求
    char buf[128];
    memset(buf, 0, sizeof(buf));
    sprintf(buf,"GET /%s HTTP/1.1\n", info.url);
    //printf("%s", buf);
    //发送
    char str[1024];
    memset(str, 9, sizeof(str));
    send(sock, buf, strlen(buf), 0);
    sprintf(str, "Host: %s:%s\n", info.addr, info.port);
    send(sock, str, strlen(str), 0);
    sprintf(str, "Connection: keep-alive\n");
    send(sock, str, strlen(str), 0);
    sprintf(str, "User- Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/53.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.4094.1 Safari/537.36\n");
    send(sock, str, strlen(str), 0);
    sprintf(str, "Accept: image/webp , image/*,*/* ;q=0.8\n");
    send(sock, str, strlen(str), 0);
    sprintf(str, "Referer: http://%s:%s/\n", info.addr, info.port);
    send(sock, str, strlen(str), 0);
    sprintf(str, "Accept- Encoding: gzip, deflate\n");
    send(sock, str, strlen(str), 0);
    sprintf(str, "Accept- Language :  zh-CN,zh;q=0.8\n\n");
    send(sock, str, strlen(str), 0);
    //printf("send 完毕\n");    
    char line[32];
    memset(line, 0, sizeof(line));
    //接收返回信息，读一行，看看是不是HTTP1.1 200 OK，是，请求成功，count_success++ , 否则请求失败
    getlin(sock, line); 
    //printf("getlin 完毕 %s\n", line);
    //clean_head(sock);

    //printf("线程处理完毕\n");
    //此时已经拿到了第一行，进行比较
    if(strcasecmp("HTTP/1.1 200 OK\n", line) == 0 || strcasecmp("HTTP1.0 200 OK\n", line) == 0) {
        //printf("请求成功\n");
        count_success++;
    } else {
        //printf("请求失败\n");
        close(sock);
    }

    //请求完成， 客户端数减一
    pthread_mutex_lock(&mutex);  
    //printf("客户端减少\n");
    c--;//加锁 
    pthread_mutex_unlock(&mutex);  
    close(sock);
    usleep(10);
    return NULL;
}

void *timeout(void* arg) {
    arg = NULL;
    sleep(30);
    printf("timeout!\n");
    exit(EXIT_FAILURE);
}

//./myab -c 线程数 -n 请求数 http://127.0.0.1:9999
int main(int argc, char* argv[])
{
    int ret = _getop(argc, argv, &info);
    if(ret == -1) {
        return 1;
    }

    struct sockaddr_in* addr = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
    addr->sin_family = AF_INET;
    addr->sin_port = htons(atoi(info.port));
    inet_pton(AF_INET, info.addr, &addr->sin_addr);

    //创建计时线程
    pthread_t id;
    pthread_create(&id, NULL, timeout, NULL);

    //循环创建线程
    while(n < info.n) {
//        usleep(10000);
        //printf("进入循环\n");
        if(c > info.c) {
            printf("c > info.c\n");
            usleep(10);
            continue;
        }

        usleep(10);
    //    printf("c = %d\n", c);
    //    printf("info.c = %d\n", info.c);

        if(c < info.c) {
            n++;
            //创建产线程,访问服务器
            pthread_t tid = 0;
            //printf("创建线程\n");
            //客户端数加一
            c++;
            pthread_create(&tid, NULL, handler, (void*)addr);
        }
    }

    while(c > 0) {
        //printf("c = %d\n", c);
        usleep(100000);
    }
    printf("总请求数：%d次\n", info.n);
    printf("成功请求：%d次\n", count_success);
    printf("失败请求：%d次\n", info.n - count_success);
    return 0;
}
